#ifndef MREQ_H_
#define MREQ_H_

#include <assert.h>
#include <bitset>
#include <iostream>

#include "../models/cache/CaffCache/sharers.h"
#include "module.h"
#include "node.h"
#include "stat.h"
#include "types.h"
/** #include "preq.h"       //Moved to below the enum for compilation reasons  */

using namespace std;

typedef enum
{
    NOP_INST = 0,
    
    // MIPS specific
    SYNC,
    LL,
    SC,

    // Set stalls
    QUEUE_MATCH_STALL,
    SET_STALL,
    REPLACEMENT_STALL,
    BEING_REPLACED_STALL,
    STALL_MSHR,

    // Entry stalls
    PERMISSION_STALL,
    TRANS_STALL,
    INV_DIR_STALL,
    DOWNGRADE_STALL,
    STALL_SET_XFER,

    // Stall retries for history
    STALL_SET_RETRY,
    STALL_ENTRY_RETRY,

    // directory requests
    D_FWD_GETX,
    D_FWD_GETS,

    // directory response
    WRITEBACK_ACK,
    
    // new from MOSI
    GETX_FAIL,  // Extremely, rare case.  See MOSI_cache.cpp for notes.

    // new for cache_to_cache sharing
    FWD_GETS_S,
    FWD_GETS_RETRY,
    FWD_GETS_FAIL,

    // New for MSI
    C_DATA_S,
    C_DATA_E,
    C_DATA_M,
    C_EVICT_ACK,
    C_EVICT_ACK_DATA_M,
    C_GETIS,
    C_GETIM,
    C_GETSM,
    C_GETOM,
    C_INV_ACK,
    C_PUTE,
    C_PUTE_OLD,
    C_PUTO,
    C_PUTO_OLD,
    C_PUTM,
    C_PUTM_OLD,
    C_PUTS,
    C_UNBLOCK_S,
    C_UNBLOCK_S_DIRTY,
    C_UNBLOCK_E,
    C_UNBLOCK_O,
    C_UNBLOCK_M,
    C_UNBLOCK_CONVERT,
    C_UNBLOCK_NO_CONVERT,
    C_REMAP_INSERT,
    C_REMAP_REMOVE,

    D_EVICT,
    D_FWD_GETIS,
    D_FWD_GETIM,
    D_INV,
    D_INV_ACK_CNT,
    D_PUTM,
    D_REMAP_INSERT,
    D_REMAP_REMOVE,
    D_WB_ACK,
    D_DOWNGRADE,
    D_UPGRADE,

    M_UNBLOCK_WB,       // TODO:  This shouldn't be used anymore!

    H_REPLACEMENT,
    H_DOWNGRADE,

    SUPPLY_DATA_S,
    SUPPLY_DATA_E,
    SUPPLY_DATA_M,

    CRUISE_MISSILE,

    MREQ_INVALID,
	MREQ_MESSAGE_NUM	// Use this to make a Stat Array of message types
} message_t;

/** Included preq.h down here after the enum def to stop compiler complaints!*/
#if SIM_WITH_ZESTO
    #include "../models/procesor/CaffProc/preq.h"
#else
//    #include "../models/procesor/CaffProc/preq.h"
#endif
class Hash_entry;

class Mreq {
public:
    Mreq (message_t msg = MREQ_INVALID,
          paddr_t addr = (paddr_t)0x0,
          Preq *preq = (Preq *)NULL,
          ModuleID src_id = (ModuleID){-1,INVALID_M},
          ModuleID dest_id = (ModuleID){-1,INVALID_M},
          ModuleID fwd_id = (ModuleID){-1,INVALID_M},
          int acks = -1);

	~Mreq ();

    message_t msg;
    paddr_t pc;
    paddr_t addr;
    Preq *preq;
    ModuleID src_mid;
    ModuleID dest_mid;
    ModuleID fwd_mid;
    int INV_ACK_count;
    timestamp_t req_time;
    bool stalled;
    timestamp_t module_port_resolve_time;

    bool is_no_net_msg;

    /** Used for cruise missle type requests.  */
    //bool cruise_missile;
    //Sharers *cm_targets;
    //Mreq *cm_request;

    /** Used for remap table updates.  */
    ModuleID home_directory;
    ModuleID replica_directory;

    static const char * message_t_str[MREQ_MESSAGE_NUM];

    /** Message type checking. */
    bool is_query (void);
    bool is_put (void);
    bool is_data_msg (void);
    bool is_to_client (void);
    bool is_bcast_hack (void);
    bool is_to_directory (void);
    bool is_response_msg (void);
    bool requires_dir_processing (void);
    bool requires_inv_dir (void);

    /** Debug.  */
    void add_to_history (Mreq *request, const char *msg=NULL);
    void print_msg (ModuleID mid, const char *add_msg);
    void preq_print_mreq (timestamp_t t, const char *add_msg);
    void dump (void);

    /** Manifold */
    static int Serialize(Mreq* request, unsigned char *req_copy);
    static Mreq* Deserialize(unsigned char *req_copy);
};

#endif /*MREQ_H_*/
